package net.ufrog.common.data.hibernate;

import net.ufrog.common.data.QueryScript;
import net.ufrog.common.data.jpa.Jpql;
import net.ufrog.common.utils.Objects;
import org.hibernate.query.NativeQuery;
import org.omg.CORBA.OBJ_ADAPTER;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;

import javax.persistence.Query;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-01-28
 * @since 0.1
 */
public class HibernateJpql extends Jpql {

    /**
     * @param sql 原生查询脚本
     * @param arguments 参数
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果对象
     */
    public <T extends Serializable> T findSingleByNative(String sql, Map<String, Object> arguments, Class<T> requiredType) {
        QueryScript qs = new QueryScript(sql);
        qs.append("", arguments);
        return findSingleByNativeQueryScript(qs, requiredType);
    }

    /**
     * @param sql 原生查询脚本
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 结果对象
     */
    public <T extends Serializable> T findSingleByNative(String sql, Class<T> requiredType, Object... arguments) {
        return findSingleByNative(sql, Objects.map(arguments), requiredType);
    }

    /**
     * @param queryScript 查询脚本对象
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果对象
     */
    public <T extends Serializable> T findSingleByNativeQueryScript(QueryScript queryScript, Class<T> requiredType) {
        Query query = createNativeQuery(queryScript.getScriptWithoutOrder(), queryScript.getArguments(), null);
        query.unwrap(NativeQuery.class).addEntity(requiredType);
        return requiredType.cast(query.getSingleResult());
    }

    /**
     * @param template 模版文件
     * @param arguments 参数
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果对象
     */
    public <T extends Serializable> T findSingleByNativeTemplate(String template, Map<String, Object> arguments, Class<T> requiredType) {
        return findSingleByNative(rendTemplate(template, arguments), arguments, requiredType);
    }

    /**
     * @param template 模版文件
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 结果对象
     */
    public <T extends Serializable> T findSingleByNativeTemplate(String template, Class<T> requiredType, Object... arguments) {
        return findSingleByNativeTemplate(template, Objects.map(arguments), requiredType);
    }

    /**
     * @param sql 原生查询脚本
     * @param arguments 参数
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果列表
     */
    public <T extends Serializable> List<T> findByNative(String sql, Map<String, Object> arguments, Class<T> requiredType) {
        QueryScript qs = new QueryScript(sql);
        qs.append("", arguments);
        return findByNativeQueryScript(qs, requiredType);
    }

    /**
     * @param sql 原生查询脚本
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 结果列表
     */
    public <T extends Serializable> List<T> findByNative(String sql, Class<T> requiredType, Object... arguments) {
        return findByNative(sql, Objects.map(arguments), requiredType);
    }

    /**
     * @param queryScript 查询脚本对象
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果列表
     */
    @SuppressWarnings("unchecked")
    public <T extends Serializable> List<T> findByNativeQueryScript(QueryScript queryScript, Class<T> requiredType) {
        Query query = createNativeQuery(queryScript.getScript(), queryScript.getArguments(), null);
        query.unwrap(NativeQuery.class).addEntity(requiredType);
        return query.getResultList();
    }

    /**
     * @param template 模版文件
     * @param arguments 参数
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 结果列表
     */
    public <T extends Serializable> List<T> findByNativeTemplate(String template, Map<String, Object> arguments, Class<T> requiredType) {
        return findByNative(rendTemplate(template, arguments), arguments, requiredType);
    }

    /**
     * @param templates 模版文件
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 结果列表
     */
    public <T extends Serializable> List<T> findByNativeTemplate(String templates, Class<T> requiredType, Object... arguments) {
        return findByNativeTemplate(templates, Objects.map(arguments), requiredType);
    }

    /**
     * @param sql 原生查询脚本
     * @param arguments 参数
     * @param pageable 分页信息
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 分页结果
     */
    @SuppressWarnings("unchecked")
    public <T extends Serializable> Page<T> findByNative(String sql, Map<String, Object> arguments, Pageable pageable, Class<T> requiredType) {
        QueryScript qs = new QueryScript(sql);
        qs.append("", arguments);
        if (!qs.hasOrder()) {
            qs.order(pageable.getSort());
        }
        return findByNativeQueryScript(qs, pageable, requiredType);
    }

    /**
     * @param sql 原生查询脚本
     * @param pageable 分页信息
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 分页结果
     */
    public <T extends Serializable> Page<T> findByNative(String sql, Pageable pageable, Class<T> requiredType, Object... arguments) {
        return findByNative(sql, Objects.map(arguments), pageable, requiredType);
    }

    /**
     * @param queryScript 查询脚本对象
     * @param pageable 分页信息
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 分页结果
     */
    @SuppressWarnings("unchecked")
    public <T extends Serializable> Page<T> findByNativeQueryScript(QueryScript queryScript, Pageable pageable, Class<T> requiredType) {
        Long count = (Long) findSingleByNative(queryScript.getCountScript(), queryScript.getArguments())[0];
        List<T> list = Collections.emptyList();
        if (count.compareTo(0L) > 0) {
            Query query = createNativeQuery(queryScript.getScript(), queryScript.getArguments(), pageable);
            query.unwrap(NativeQuery.class).addEntity(requiredType);
            list = query.getResultList();
        }
        return new PageImpl<>(list, pageable, count);
    }

    /**
     * @param template 模版文件
     * @param arguments 参数
     * @param pageable 分页信息
     * @param requiredType 请求类型
     * @param <T> 请求泛型
     * @return 分页结果
     */
    public <T extends Serializable> Page<T> findByNativeTemplate(String template, Map<String, Object> arguments, Pageable pageable, Class<T> requiredType) {
        return findByNative(rendTemplate(template, arguments), arguments, pageable, requiredType);
    }

    /**
     * @param template 模版文件
     * @param pageable 分页信息
     * @param requiredType 请求类型
     * @param arguments 参数
     * @param <T> 请求泛型
     * @return 分页结果
     */
    public <T extends Serializable> Page<T> findByNativeTemplate(String template, Pageable pageable, Class<T> requiredType, Object... arguments) {
        return findByNativeTemplate(template, Objects.map(arguments), pageable, requiredType);
    }
}
